package com.laviedesfons.jhmtasc.metadata;

import com.laviedesfons.jhmtasc.exceptions.MException;
import com.laviedesfons.jhmtasc.types.interfaces.InternalType;
import java.util.Arrays;

/**
 *
 * @author Romano
 */
public class MetaType {

    private MetaType[] params = null;
    private String camlType;
    private String name;
    private Class<? extends InternalType> clazz;
    private boolean isPrimitive = true;
    private boolean isAlias = false;
    private Factory factory = null;

    static interface Factory {
        InternalType create(MetaType m);
    }

    MetaType(){}

    // Package protected

    private static String stripParams(String c){
        if (c.contains("<")) {
            return c.substring(0, c.indexOf("<"));
        }
        return c;
    }

    MetaType(String c) {
        name = stripParams(c);
    }

    final void setClazz(Class<? extends InternalType> c){
        clazz = c;
    }

    final void setFactory(Factory f) {
        this.factory = f;
    }

    final void setIsPrimitive(boolean isPrim){
        isPrimitive = isPrim;
    }

    final boolean isAlias(){
        return isAlias;
    }

    final void setIsAlias(boolean b){
        isAlias = b;
    }

    final String getName() {
        if(name != null){
            return name;
        } else if(clazz != null){
            return stripParams(clazz.getName());
        }
        throw new IllegalStateException("MetaType not bound");
    }

    final String getSimpleName(){
        String n = getName();
        int idx = n.lastIndexOf('.');
        if(idx != -1)
            return n.substring(n.lastIndexOf('.') + 1);
        return n;
    }

    final String getFullName() {
        StringBuilder sb = new StringBuilder(name);
        if(isGeneric() && ! isAlias()){
            sb.append("<");
            for(int i=0; i < params.length ; i++){
                sb.append(params[i].getFullName());
                if(i < params.length -1)
                    sb.append(",");
            }
            sb.append(">");
        }
        return sb.toString();
    }

    final String getCamlType() {
        return camlType;
    }

    final void setCamlType(String c) {
        this.camlType = c;
    }


    final void setParams(MetaType[] params) {
        this.params = params;
    }

    // Public

    public MetaType(Class<? extends InternalType> cl){
        clazz = cl;
    }

    public final boolean isPrimitive(){
        return isPrimitive;
    }

    public final Boolean isGeneric() {
        return params != null && params.length >0;
    }

    public final MetaType[] getParams() {
        return params;
    }

    public Class<? extends InternalType> getClazz() {
        return clazz;
    }

    public InternalType create() {
        if(factory != null){
            return factory.create(this);
        }
        throw new RuntimeException(new MException("unable to create an InternalType"));
    }

    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder(MetaType.class.getSimpleName());
        sb.append("{ className=");
        sb.append(getName());
        if(isGeneric()){
            sb.append(" [");
            for(MetaType m : params){
                sb.append(m.toString());
            }
            sb.append("]");
        }
        return sb.append("}").toString();
    }

    @Override
    public boolean equals(Object obj) {
        if (obj == null) {
            return false;
        }
        if (getClass() != obj.getClass()) {
            return false;
        }
        final MetaType other = (MetaType) obj;
        return true;
    }

    @Override
    public int hashCode() {
        int hash = 5;
        hash = 89 * hash + Arrays.deepHashCode(this.params);
        hash = 89 * hash + (this.clazz != null ? this.clazz.hashCode() : 0);
        hash = 89 * hash + (this.isPrimitive ? 1 : 0);
        return hash;
    }

}
